﻿/* Autoduel Game
 * CS4911 Senior Design Project
 * Fall 2010 Semester
 * Ian Axelrod, Liz Ha, Andrew Muldowney, Edward Futch
 */
using System;
using System.IO;
using System.Text;

using AutoduelClient.GameClient.Locations;
using AutoduelClient.GameClient.Network;
using AutoduelClient.GameClient.World;
using AutoduelClient.GameUI;

using Microsoft.Xna.Framework;

namespace AutoduelClient.GameClient.Player
{
    /// <summary>
    /// A delegate type for hooking up location found notifications
    /// </summary>
    /// <param name="Sender"></param>
    /// <param name="e"></param>
    public delegate void LocationFoundEventHandler(object Sender, LocationFoundEventArgs e);

    /// <summary>
    /// Represents a player in the autoduel world
    /// </summary>
    public class Player
    {
		#region Static Fields (2)
		/// <summary>
        /// Defines the maximum armor a player is allowed to have
        /// </summary>
        public const int MAX_ARMOR = 3;
		/// <summary>
        /// Defines the maximum health a player is allowed to have
        /// </summary>
        public const int MAX_HEALTH = 3;
		#endregion

		#region Static Methods (1)
		/// <summary>
        /// Creates a player object from the packet presented
        /// </summary>
        /// <param name="packet">the packet to decode</param>
        /// <returns>The player object created from the packet</returns>
        public static Player DecodePacket(byte[] packet)
        {
            int i = 1;
            Player player = new Player();

            switch (packet[0])
            {
                case (byte)OPCODES.ORD_UPDPLAYER:
                    {
                        player.Name = Util.MakeString(packet, ref i);
                        player.MechanicSkill = Util.ReverseInt32(BitConverter.ToInt32(packet, i));
                        player.Health = Util.ReverseInt32(BitConverter.ToInt32(packet, i + 4));
                        player.CurrentCoord = new Coordinate(Util.ReverseInt32(BitConverter.ToInt32(packet, i + 8)), 
                            Util.ReverseInt32(BitConverter.ToInt32(packet, i + 12)));
                        player.InLocation = (packet[i + 16] == 1);
                        break;
                    }
                case (byte)OPCODES.ORD_UPDPLAYERLIST:
                case (byte)OPCODES.ORD_REMPLAYER:
                    {
                        player.Name = Util.MakeString(packet, ref i);
                        break;
                    }
            }

            return player;
        }
		#endregion

		#region Fields (12)
        // marker to indicate whether or not the player
        // is inside of a location
        private bool inLocation;

        /// <summary>
        /// Get/Set marker to indicate whether or not
        /// the player is inside of a location
        /// </summary>
        public bool InLocation
        {
            get { return inLocation; }
            set { inLocation = value; }
        }

		// the current car the player is in
        private Car currentCar;

		// the grid this player is in
        private Grid currentGrid;

		// the sprite used to represent a player
        private AnimatedSprite playerSprite;

		// the client associated with us
        private SimpleClient client;

		// the position of the player
        private Coordinate currentCoord;
		// the movement of the player
        private Point movement;

		// the amount of money the player has
        private int credits;
		// the health of the player
        private int health;
		// the skill of the player in mechanics
        private int mechanicSkill;

		// the sprite path for the player's model
        private string image;
		// the name of the player
        private string name;

        // a reference to the ui layer
        private AutoduelClient.GameUI.Game game;

        /// <summary>
        /// Get/Set the reference to the ui layer
        /// </summary>
        public AutoduelClient.GameUI.Game Game
        {
            get { return game; }
            set { game = value; }
        }
		#endregion

        // The current location of the player, if he is in one
        private Location currentLocation;

        // The location nearest to us (it must be close!)
        private Location nearestLocation;

        /// <summary>
        /// Get/Set the location nearest to the player
        /// </summary>
        public Location NearestLocation
        {
            get { return nearestLocation; }
            set { nearestLocation = value; }
        }

        /// <summary>
        /// Get/Set the current location of the player
        /// </summary>
        public Location CurrentLocation
        {
            get { return currentLocation; }
            set { currentLocation = value; }
        }

		#region Properties (12)
		/// <summary>
        /// Gets/Sets the client associated with this player
        /// </summary>
        public SimpleClient Client
        {
            get { return client; }
            set { client = value; }
        }
		/// <summary>
        /// Get/Set the amount of money the player has
        /// </summary>
        public int Credits
        {
            get { return credits; }
            set { credits = value; }
        }
		/// <summary>
        /// Get/Set the current car the player is in
        /// </summary>
        public Car CurrentCar
        {
            get { return currentCar; }
            set { currentCar = value; }
        }
		/// <summary>
        /// Gets/Sets the location of the player on the map
        /// </summary>
        public Coordinate CurrentCoord
        {
            get { return currentCoord; }
            set { currentCoord = value; }
        }
		/// <summary>
        /// Gets/Sets the current grid player is in
        /// </summary>
        public Grid CurrentGrid
        {
            get { return currentGrid; }
            set { currentGrid = value; }
        }
		/// <summary>
        /// Gets/Sets the health of the player
        /// </summary>
        public int Health
        {
            get { return health; }
            set { health = value; }
        }
		/// <summary>
        /// Gets/Sets the sprite path for the player's model
        /// </summary>
        public string Image
        {
            get { return image; }
            set { image = value; }
        }
		/// <summary>
        /// Gets/Sets the mechanic skill of the player
        /// </summary>
        public int MechanicSkill
        {
            get { return mechanicSkill; }
            set { mechanicSkill = value; }
        }
		/// <summary>
        /// Gets/Sets the movement of the player on the map
        /// </summary>
        public Point Movement
        {
            get { return movement; }
            set { movement = value; }
        }
		/// <summary>
        /// Gets/Sets the name of the player
        /// </summary>
        public string Name
        {
            get { return name; }
            set { name = value; }
        }
		/// <summary>
        /// Gets/Sets the sprite used to represent this player
        /// </summary>
        internal AnimatedSprite PlayerSprite
        {
            get { return playerSprite; }
            set { playerSprite = value; }
        }
		#endregion

		#region Constructors (2)
		/// <summary>
        /// Construct a player
        /// </summary>
        public Player() {}

		/// <summary>
        /// Construct a player
        /// </summary>
        /// <param name="name">The name of the player</param>
        /// <param name="mechanicSkill">The mechanic skill of the player</param>
        /// <param name="health">The health of the player</param>
        /// <param name="currentCoord">The location of the player</param>
        /// <param name="image">The image path for the player</param>
        /// <param name="client">The client associated with this player</param>
        public Player(string name, int mechanicSkill, int health, 
            Coordinate currentCoord, string image, SimpleClient client, AutoduelClient.GameUI.Game game)
        {
            this.name = name;
            this.mechanicSkill = mechanicSkill;
            this.health = health;
            this.currentCoord = currentCoord;
            this.image = image;
            this.client = client;
            this.game = game;
        }
		#endregion

		#region Methods (3)

		/// <summary>
        /// Encode a player packet
        /// </summary>
        /// <returns>The encoded player packet</returns>
        public byte[] EncodePacket(OPCODES opcode)
        {
            MemoryStream stream = new MemoryStream();
            using (BinaryWriter writer = new BinaryWriter(stream))
            {
                writer.Write((byte)opcode);
                switch (opcode)
                {
                    case OPCODES.ORD_UPDPLAYER:
                        {
                            writer.Write(Encoding.Convert(Encoding.Unicode, Encoding.ASCII, Encoding.Unicode.GetBytes(this.name)));
                            writer.Write(Util.PKT_DLM);
                            writer.Write(this.mechanicSkill);
                            writer.Write(this.health);
                            writer.Write(this.currentCoord.X);
                            writer.Write(this.currentCoord.Y);
                            writer.Write(this.inLocation);
                            writer.Write(Util.PKT_DLM);
                            break;
                        }
                    case OPCODES.ORD_REMPLAYER:
                    case OPCODES.ORD_BANPLAYER:
                        {
                            writer.Write(Encoding.Convert(Encoding.Unicode, Encoding.ASCII, Encoding.Unicode.GetBytes(this.name)));
                            writer.Write(Util.PKT_DLM);
                            break;
                        }
                }
            }
            return stream.ToArray();
        }

        /// <summary>
        /// An event that is fired whenever a location near to the player
        /// is found
        /// </summary>
        public event LocationFoundEventHandler LocationFound;

        /// <summary>
        /// Invoke the LocationFound event
        /// </summary>
        /// <param name="e"></param>
        private void OnLocationFound(LocationFoundEventArgs e)
        {
            if (LocationFound != null)
            {
                LocationFound(this, e);
            }
        }

		/// <summary>
        /// Offsets the client coordinates according to the vector
        /// </summary>
        /// <param name="vect">Vector that determines how to modify the coordinates</param>
        public void Move(Coordinate vect)
        {
            // update our current coordinates
            this.currentCoord.X += vect.X;
            this.currentCoord.Y += vect.Y;

            if ((this.currentCoord.X / World.Grid.GRID_WIDTH) >= World.World.GRIDS_X)
            {
                this.currentCoord.X = 0;
            }
            if ((this.currentCoord.Y / World.Grid.GRID_HEIGHT) >= World.World.GRIDS_Y)
            {
                this.currentCoord.Y = 0;
            }
            if (this.currentCoord.Y < 0)
            {
                this.currentCoord.Y = World.Grid.GRID_HEIGHT * (World.World.GRIDS_Y);
            }
            if (this.currentCoord.X < 0)
            {
                this.currentCoord.X = World.Grid.GRID_WIDTH * (World.World.GRIDS_X);
            }
            System.Diagnostics.Debug.WriteLine("Current Coordinates: " + this.currentCoord);

            // if we are in a car, update its coordinates
            if (this.currentCar != null)
            {
                this.currentCar.CurrentCoord = this.currentCoord;
                // TODO: let the server know the car's position has changed
            }


            // let the server know we have updated
            this.client.SessionSend(this.EncodePacket(OPCODES.ORD_UPDPLAYER));

            // check to see if we are near a location
            Location location = this.currentGrid.NearLocation(this.currentCoord);

            this.nearestLocation = location;

            // if we are, ask the player if he wants to enter
            if (location != null)
            {
                OnLocationFound(new LocationFoundEventArgs(location));
            }
        }

		/// <summary>
        /// Represents the player object as a string
        /// </summary>
        /// <returns>The string representation of the player object</returns>
        public override string ToString()
        {
            return this.name + " -- mechanicSkill: " + this.mechanicSkill +
                " -- health: " + this.health + " -- currentCoord(x, y): (" +
                this.currentCoord.X + ", " + this.currentCoord.Y + ") -- inLocation: " +
                this.inLocation;
        }

		#endregion
    }
}
